package br.unip.tcc.domotic.util;

import java.io.File;
import java.io.FileInputStream;
import java.lang.annotation.Annotation;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Set;
import java.util.jar.JarEntry;
import java.util.jar.JarInputStream;

public class ClassScanner {
	
	public static Set<Class<?>> getClasses(String packageName) {
		ClassLoader loader = Thread.currentThread().getContextClassLoader();
		return getClasses(loader, packageName);
	}
	
	public static Set<Class<?>> findByAnnotations(String packageName, Class<? extends Annotation>... annotation){
		Set<Class<?>> classes = new HashSet<Class<?>>();
		
		try {
			Set<Class<?>> allClasses = getClasses(packageName);
			for (Class<?> clazz : allClasses){
				boolean containsAnnotation = false;
				for (Class<? extends Annotation> annotationParam : annotation){
					if (clazz.getAnnotation(annotationParam) != null){
						containsAnnotation = true;
					}
				}
				if (containsAnnotation)classes.add(clazz);
			}
		} catch (Exception e) {
			//e.printStackTrace();
		}
		return classes;
	}
	
	public static Set<Class<?>> getClasses(ClassLoader loader, String packageName) {
		Set<Class<?>> classes = new HashSet<Class<?>>();
		try {
			String path = packageName.replace('.', '/');
			Enumeration<URL> resources = loader.getResources(path);
			if (resources != null) {
				while (resources.hasMoreElements()) {
					String filePath = resources.nextElement().getFile();
					// WINDOWS HACK
					if(filePath.indexOf("%20") > 0) filePath = filePath.replaceAll("%20", " ");
					if (filePath != null) {
						if ((filePath.indexOf("!") > 0) & (filePath.indexOf(".jar") > 0)) {
							String jarPath = filePath.substring(0, filePath.indexOf("!")).substring(filePath.indexOf(":") + 1);
							// WINDOWS HACK
							if (jarPath.indexOf(":") >= 0) jarPath = jarPath.substring(1); 
							classes.addAll(getFromJARFile(jarPath, path));
						} else {
							classes.addAll(getFromDirectory(new File(filePath), packageName));
						}
					}
				}
			}
		} catch (Exception e) {
			//e.printStackTrace();
		}
		return classes;
	}
	
	private static Set<Class<?>> getFromDirectory(File directory, String packageName) {
		Set<Class<?>> classes = new HashSet<Class<?>>();
		if (directory.exists()) {
			for (String file : directory.list()) {
				if (file.endsWith(".class")) {
					String name = packageName + '.' + file.replace(".class", "").trim();
					try {
						Class<?> clazz = Class.forName(name);
						classes.add(clazz);
					} catch (Exception e) {
						//e.printStackTrace();
					}
				}
			}
		}
		return classes;
	}
	
	private static Set<Class<?>> getFromJARFile(String jar, String packageName) {
		Set<Class<?>> classes = new HashSet<Class<?>>();
		try {
			JarInputStream jarFile = new JarInputStream(new FileInputStream(jar));
			JarEntry jarEntry;
			while ((jarEntry = jarFile.getNextJarEntry()) != null){
				String className = jarEntry.getName();
				if (className.endsWith(".class")) {
					className = className.replace(".class", "").trim();
					try {
						if (className.startsWith(packageName)) classes.add(Class.forName(className.replace('/', '.')));
					} catch (Exception e) {
						//e.printStackTrace();
					}
				}
			}
		} catch (Exception e) {
			//e.printStackTrace();
		}
		return classes;
	}

}
